#include "Arduino.h"
#include "stdbool.h"
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "Wire.h"

extern "C" void checkIOAvail(lua_State *L, int pin);

static int lua_i2c_setPins(lua_State *L)
{
    int sda = luaL_checkinteger(L, 1);
    int scl = luaL_checkinteger(L, 2);
    checkIOAvail(L, sda);
    checkIOAvail(L, scl);
    Wire1.setPins(sda, scl);
    return 0;
}

static int lua_i2c_setClock(lua_State *L)
{
    int freq = luaL_checkinteger(L, 1);
    Wire1.setClock(freq);
    return 0;
}

static int lua_i2c_read(lua_State *L)
{
    int size = luaL_optnumber(L, 1, 1);
    char *buf = (char *)ps_malloc(size);
    size = Wire1.readBytes(buf, size);
    lua_pushlstring(L, buf, size);
    free(buf);
    return 1;
}

static int lua_i2c_write(lua_State *L)
{
    size_t s = 0;
    const char *data = luaL_checklstring(L, 1, &s);
    s = Wire1.write((const uint8_t *)data, s);
    lua_pushinteger(L, s);
    return 1;
}

static int lua_i2c_beginT(lua_State *L)
{
    int addr = luaL_checkinteger(L, 1);
    Wire1.beginTransmission(addr);
    return 0;
}

static int lua_i2c_endT(lua_State *L)
{
    int res = Wire1.endTransmission();
    lua_pushboolean(L, res == 0);
    return 1;
}

/*
i2c读寄存器数据
@api i2c.readReg(addr, reg, len)
@int I2C子设备的地址, 7位地址
@int 寄存器地址
@int 待接收的数据长度
@integer 可选参数 是否发送停止位 1发送 0不发送 默认发送
@return string 收到的数据
@usage@int 设备id, 例如i2c1的id为1, i2c2的id为2

-- 从i2c地址为0x5C的设备的寄存器0x01读出2个字节的数据
i2c.readReg(0x5C, 0x01, 2)
*/
static int l_i2c_read_reg(lua_State *L)
{
    int addr = luaL_checkinteger(L, 1);
    int reg = luaL_checkinteger(L, 2);
    int len = luaL_checkinteger(L, 3);
    int stop = luaL_optnumber(L, 4, 1);
    Wire1.beginTransmission(addr);
    Wire1.write(reg);
    uint8_t r = Wire1.endTransmission(stop);
    if (r != 0)
    {
        // 如果返回值不为0，说明收失败了
        lua_pushlstring(L, NULL, 0);
        return 1;
    }
    char *buff = (char *)ps_malloc(sizeof(char) * len + 1);
    r = Wire1.requestFrom(addr, len);
    if (r != 0)
    {
        // 如果返回值不为0，说明收失败了
        len = 0;
    }
    len = Wire1.readBytes(buff, len);
    lua_pushlstring(L, buff, len);
    free(buff);
    return 1;
}
/*
i2c写寄存器数据
@api i2c.writeReg(addr, reg, data,stop)
@int I2C子设备的地址, 7位地址
@int 寄存器地址
@string 待发送的数据
@integer 可选参数 是否发送停止位 1发送 0不发送 默认发送(105不支持)
@return true/false 发送是否成功
@usage
-- 从i2c地址为0x5C的设备的寄存器0x01写入2个字节的数据
i2c.writeReg(0x5C, 0x01, string.char(0x00, 0xF2))
*/
static int lua_i2c_write_reg(lua_State *L)
{
    int id = 0;
    int addr = luaL_checkinteger(L, 1);
    int reg = luaL_checkinteger(L, 2);
    size_t len;
    const char *lb = luaL_checklstring(L, 3, &len);
    int stop = luaL_optnumber(L, 4, 1);
    Wire1.beginTransmission(addr);
    Wire1.write(reg);
    Wire1.write((const uint8_t *)lb, len);
    uint8_t r = Wire1.endTransmission(stop);
    lua_pushboolean(L, r == 0);
    return 1;
}

static int lua_i2c_scan(lua_State *L)
{
    uint8_t addrList[128];
    uint8_t nDevices = 0;
    int addr;
    for (addr = 0x01; addr < 0x7f; ++addr)
    {
        Wire1.beginTransmission(addr);
        uint8_t error = Wire1.endTransmission();
        if (error == 0)
        {
            addrList[nDevices] = addr;
            nDevices++;
        }
    }
    lua_pushlstring(L, (const char *)addrList, nDevices);
    return 1;
}

static const luaL_Reg I2CLib[] = {

    {"setPins", lua_i2c_setPins},
    {"setClock", lua_i2c_setClock},
    {"beginT", lua_i2c_beginT},
    {"write", lua_i2c_write},
    {"read", lua_i2c_read},
    {"endT", lua_i2c_endT},
    {"readReg", lua_i2c_write_reg},
    {"writeReg", lua_i2c_write_reg},
    {"scan", lua_i2c_scan},
    {NULL, NULL},
};

extern "C" int luaopen_i2c(lua_State *L)
{
    luaL_newlib(L, I2CLib);
    return 1;
}
